"""
文件处理工具模块 - 函数式设计
处理文件读取、筛选和基本操作

作者: Claude
创建时间: 2024-09-14
"""

import os
import re
from typing import List, Tuple, Generator, Optional, Set, Dict
from pathlib import Path
from functools import partial


def get_video_extensions() -> Set[str]:
    """
    获取支持的视频文件扩展名
    
    :return: 视频扩展名集合
    """
    return {'.mp4', '.avi', '.mkv', '.wmv', '.mov', '.flv', '.m4v', '.ts', '.m2ts'}


def is_video_file(filename: str) -> bool:
    """
    检查是否为视频文件 - 纯函数
    
    :param filename: 文件名
    :return: 是否为视频文件
    """
    if not filename:
        return False
    
    file_extension = Path(filename).suffix.lower()
    return file_extension in get_video_extensions()


def extract_filename_from_tree_line(line: str) -> Optional[str]:
    """
    从目录树行中提取文件名 - 纯函数
    
    :param line: 目录树中的一行
    :return: 提取的文件名或None（不含扩展名）
    """
    if not line or not line.strip():
        return None
    
    # 清理行内容
    cleaned_line = line.strip()
    
    # 查找 |- 分隔符后的文件名
    if '|-' in cleaned_line:
        filename = cleaned_line.split('|-')[-1].strip()
    else:
        return None
    
    # 检查是否为视频文件
    if filename and is_video_file(filename):
        # 返回完整文件名，保留扩展名（作为原始文件名）
        return filename
    
    return None


def read_directory_tree_file(file_path: str) -> List[str]:
    """
    读取目录树文件并提取视频文件名
    
    :param file_path: 目录树文件路径
    :return: 视频文件名列表
    """
    if not os.path.exists(file_path):
        raise FileNotFoundError(f"文件不存在: {file_path}")
    
    video_files = []
    
    try:
        # 尝试多种编码
        encodings = ['utf-8', 'gbk', 'gb2312', 'latin-1']
        
        for encoding in encodings:
            try:
                with open(file_path, 'r', encoding=encoding) as f:
                    for line_number, line in enumerate(f, 1):
                        try:
                            filename = extract_filename_from_tree_line(line)
                            if filename:
                                video_files.append(filename)
                        except Exception as e:
                            # 记录但不中断处理
                            print(f"警告: 处理第{line_number}行时出错: {e}")
                            continue
                break  # 成功读取，跳出编码循环
            except UnicodeDecodeError:
                continue  # 尝试下一个编码
        
    except Exception as e:
        raise RuntimeError(f"读取文件时出错: {e}")
    
    return video_files


def read_file_list_from_text(file_path: str) -> List[str]:
    """
    从文本文件读取文件名列表
    
    :param file_path: 文本文件路径
    :return: 文件名列表
    """
    if not os.path.exists(file_path):
        raise FileNotFoundError(f"文件不存在: {file_path}")
    
    try:
        with open(file_path, 'r', encoding='utf-8') as f:
            # 过滤空行和注释行
            lines = [line.strip() for line in f if line.strip() and not line.startswith('#')]
            # 只保留视频文件
            return [line for line in lines if is_video_file(line)]
    except Exception as e:
        raise RuntimeError(f"读取文件列表时出错: {e}")


def scan_directory_for_videos(directory_path: str, recursive: bool = False) -> List[str]:
    """
    扫描目录中的视频文件
    
    :param directory_path: 目录路径
    :param recursive: 是否递归扫描子目录
    :return: 视频文件名列表（相对路径）
    """
    if not os.path.exists(directory_path):
        raise FileNotFoundError(f"目录不存在: {directory_path}")
    
    if not os.path.isdir(directory_path):
        raise ValueError(f"路径不是目录: {directory_path}")
    
    video_files = []
    
    try:
        if recursive:
            # 递归扫描
            for root, dirs, files in os.walk(directory_path):
                for file in files:
                    if is_video_file(file):
                        # 返回相对路径
                        relative_path = os.path.relpath(os.path.join(root, file), directory_path)
                        video_files.append(relative_path)
        else:
            # 只扫描当前目录
            for file in os.listdir(directory_path):
                file_path = os.path.join(directory_path, file)
                if os.path.isfile(file_path) and is_video_file(file):
                    video_files.append(file)
    except Exception as e:
        raise RuntimeError(f"扫描目录时出错: {e}")
    
    return sorted(video_files)


def filter_files_by_pattern(files: List[str], pattern: str) -> List[str]:
    """
    按正则表达式模式筛选文件 - 函数式筛选
    
    :param files: 文件列表
    :param pattern: 正则表达式模式
    :return: 筛选后的文件列表
    """
    try:
        regex = re.compile(pattern, re.IGNORECASE)
        return list(filter(lambda f: regex.search(f), files))
    except re.error as e:
        raise ValueError(f"无效的正则表达式模式: {pattern}, 错误: {e}")


def filter_files_by_extension(files: List[str], extensions: List[str]) -> List[str]:
    """
    按扩展名筛选文件 - 函数式筛选
    
    :param files: 文件列表
    :param extensions: 扩展名列表
    :return: 筛选后的文件列表
    """
    extensions_lower = [ext.lower() for ext in extensions]
    
    def has_target_extension(filename: str) -> bool:
        """检查文件是否有目标扩展名"""
        return Path(filename).suffix.lower() in extensions_lower
    
    return list(filter(has_target_extension, files))


def group_files_by_series(files: List[str]) -> Dict[str, List[str]]:
    """
    按系列分组文件 - 函数式分组
    
    :param files: 文件列表
    :return: 按系列分组的文件字典
    """
    from collections import defaultdict
    
    groups = defaultdict(list)
    
    # 系列识别模式
    series_patterns = [
        r'^([A-Z]+)-\d+',  # 有码系列
        r'^FC2-(\d+)',     # FC2系列
        r'^(\d{6})_\d+',   # 无码日期系列
    ]
    
    def extract_series(filename: str) -> str:
        """提取系列名称"""
        for pattern in series_patterns:
            match = re.search(pattern, filename.upper())
            if match:
                return match.group(1)
        return "OTHER"  # 未识别系列
    
    # 分组处理
    for file in files:
        series = extract_series(file)
        groups[series].append(file)
    
    return dict(groups)


def batch_rename_files(file_mapping: List[Tuple[str, str]], dry_run: bool = True) -> List[Tuple[str, str, bool]]:
    """
    批量重命名文件
    
    :param file_mapping: [(原文件名, 新文件名), ...] 
    :param dry_run: 是否为试运行模式
    :return: [(原文件名, 新文件名, 是否成功), ...]
    """
    results = []
    
    for original, new_name in file_mapping:
        try:
            if dry_run:
                # 试运行模式，只检查但不实际重命名
                if os.path.exists(original):
                    results.append((original, new_name, True))
                else:
                    results.append((original, new_name, False))
            else:
                # 实际重命名
                if os.path.exists(original):
                    os.rename(original, new_name)
                    results.append((original, new_name, True))
                else:
                    results.append((original, new_name, False))
        except Exception as e:
            print(f"重命名失败: {original} -> {new_name}, 错误: {e}")
            results.append((original, new_name, False))
    
    return results


def create_backup_list(files: List[str], backup_suffix: str = ".backup") -> List[Tuple[str, str]]:
    """
    创建文件备份列表
    
    :param files: 文件列表
    :param backup_suffix: 备份后缀
    :return: [(原文件, 备份文件), ...]
    """
    def create_backup_name(filename: str) -> str:
        """生成备份文件名"""
        path = Path(filename)
        return str(path.with_suffix(path.suffix + backup_suffix))
    
    return [(file, create_backup_name(file)) for file in files]


def validate_file_paths(files: List[str]) -> Tuple[List[str], List[str]]:
    """
    验证文件路径有效性
    
    :param files: 文件路径列表
    :return: (有效文件列表, 无效文件列表)
    """
    valid_files = []
    invalid_files = []
    
    for file in files:
        try:
            if os.path.exists(file) and os.path.isfile(file):
                valid_files.append(file)
            else:
                invalid_files.append(file)
        except Exception:
            invalid_files.append(file)
    
    return valid_files, invalid_files


# 偏函数工厂
def create_file_filter(pattern: str) -> callable:
    """
    创建文件筛选器函数
    
    :param pattern: 筛选模式
    :return: 筛选函数
    """
    return partial(filter_files_by_pattern, pattern=pattern)


def create_extension_filter(extensions: List[str]) -> callable:
    """
    创建扩展名筛选器函数
    
    :param extensions: 扩展名列表
    :return: 筛选函数
    """
    return partial(filter_files_by_extension, extensions=extensions)


# 生成器版本 - 内存友好的大文件处理
def read_directory_tree_file_generator(file_path: str) -> Generator[str, None, None]:
    """
    生成器版本的目录树文件读取 - 内存友好
    
    :param file_path: 目录树文件路径
    :yield: 视频文件名
    """
    if not os.path.exists(file_path):
        raise FileNotFoundError(f"文件不存在: {file_path}")
    
    encodings = ['utf-8', 'gbk', 'gb2312', 'latin-1']
    
    for encoding in encodings:
        try:
            with open(file_path, 'r', encoding=encoding) as f:
                for line in f:
                    filename = extract_filename_from_tree_line(line)
                    if filename:
                        yield filename
            break  # 成功读取，跳出编码循环
        except UnicodeDecodeError:
            continue  # 尝试下一个编码


# 导出的函数式接口
__all__ = [
    "read_directory_tree_file",
    "read_file_list_from_text", 
    "scan_directory_for_videos",
    "filter_files_by_pattern",
    "filter_files_by_extension",
    "group_files_by_series",
    "batch_rename_files",
    "validate_file_paths",
    "is_video_file",
    "create_file_filter",
    "create_extension_filter",
    "read_directory_tree_file_generator"
]